﻿using CCWin;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AutoUpdate
{
    public partial class UpdateForm : CCSkinMain
    {
        private int totalSize;
        private int position;
        private Stream stream;
        private Configuration config; //配置文件操作对象
        private Dictionary<string, string> ConfigItems;//配置文件字典数据


        public UpdateForm()
        {
            InitializeComponent();
            this.StartPosition = FormStartPosition.CenterScreen;
            this.Text = "自动更新";
            this.BackColor = ColorTranslator.FromHtml("#3d3d3d");
            this.Load += new EventHandler(this.Update_Load);
            this.FormClosing += new FormClosingEventHandler(this.Update_FormClosing);
        }

        /// <summary>
        /// 通过http下载更新包
        /// </summary>
        private void HttpUpdate()
        {
            Boolean DownStatus = true;
            List<UploadModel> units = JsonHelper.JSONToObject<List<UploadModel>>(ConfigItems["UploadJson"]);
            foreach (var item in units)
            {
                DownStatus = Download(item);
                if (!DownStatus)
                    break;
            }

            if (DownStatus)
            {
                MessageBox.Show("更新成功！");                
            }
            else
            {
                MessageBox.Show("下载更新资源失败！");
            }
            //启动主程序
            Process myprocess = new Process();
            ProcessStartInfo startInfo = new ProcessStartInfo($@"{AppDomain.CurrentDomain.BaseDirectory}{ConfigItems["Exe"]}", "default");
            myprocess.StartInfo = startInfo;

            //通过以下参数可以控制exe的启动方式，具体参照 myprocess.StartInfo.下面的参数，如以无界面方式启动exe等
            myprocess.StartInfo.UseShellExecute = false;
            myprocess.Start();

            Environment.Exit(0);
            //Application.Exit();
        }


        /// <summary>
        /// 删除更新包源文件
        /// </summary>
        /// <param name="dir"></param>
        public static void DeleteFolder(string dir)
        {
            DirectoryInfo d = new DirectoryInfo(dir);
            FileSystemInfo[] fsinfos = d.GetFileSystemInfos();
            foreach (FileSystemInfo fsinfo in fsinfos)
            {
                if (fsinfo is DirectoryInfo)     //判断是否为文件夹
                {
                    DeleteFolder(fsinfo.FullName);//递归调用
                }
                else if (fsinfo.Extension.Equals(".zip", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (fsinfo.Attributes.ToString().IndexOf("ReadOnly") != -1)
                        fsinfo.Attributes = FileAttributes.Normal;
                    File.Delete(fsinfo.FullName);//直接删除其中的文件
                }
            }
        }

        #region 下载文件

        /// <summary>
        /// Http方式下载文件
        /// </summary>
        /// <param name="url">http地址</param>
        /// <param name="localfile">本地文件</param>
        /// <returns></returns>
        public bool Download(UploadModel unit)
        {
            bool flag = false;
            long startPosition = 0; // 上次下载的文件起始位置
            FileStream writeStream; // 写入本地文件流对象
            unit.DownUrl = unit.DownUrl.Replace('\\', '/');

            totalSize = GetHttpLength($@"{ConfigItems["DownUrl"]}{unit.DownUrl}");// 取得远程文件长度
            string filename = Path.GetFileName(unit.DownUrl);
            string filePath = $@"{AppDomain.CurrentDomain.BaseDirectory}{unit.UnZipUrl}";
            if (!System.IO.Directory.Exists(filePath))
            {
                Directory.CreateDirectory(filePath);
                //MessageBox.Show("指定的目录: " + TargetDirectory + " 不存在!");
            }
            if (totalSize == 745 || totalSize == 0)
            {
                MessageBox.Show("远程文件不存在.");
                return false;
            }

            label1.Invoke(new Action(() =>
            {
                label1.Text = "正在下载:" + unit.UnitName + " 大小:" + unit.FileSize;
            }));

            File.Delete($@"{filePath}/{filename}");
            // 判断要下载的文件夹是否存在
            if (File.Exists($@"{filePath}/{filename}"))
            {
                writeStream = File.OpenRead($@"{filePath}/{filename}");             // 存在则打开要下载的文件
                startPosition = writeStream.Length;
                stream = writeStream;// 获取已经下载的长度
                if (startPosition >= totalSize)
                {
                    //删除本地同名文件
                    File.Delete($@"{filePath}/{filename}");
                    startPosition = 0;
                }
                else
                {
                    writeStream.Seek(startPosition, SeekOrigin.Current); // 本地文件写入位置定位
                }
            }
            else
            {
                writeStream = new FileStream($@"{filePath}/{filename}", FileMode.Create);// 文件不保存创建一个文件
                startPosition = 0;
            }

            try
            {
                HttpWebRequest myRequest = (HttpWebRequest)HttpWebRequest.Create($@"{ConfigItems["DownUrl"]}{unit.DownUrl}");// 打开网络连接
                HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
                if (startPosition > 0)
                {
                    myRequest.AddRange((int)startPosition);// 设置Range值,与上面的writeStream.Seek用意相同,是为了定义远程文件读取位置
                }
                Stream readStream = myResponse.GetResponseStream();// 向服务器请求,获得服务器的回应数据流
                byte[] btArray = new byte[512];// 定义一个字节数据,用来向readStream读取内容和向writeStream写入内容
                int contentSize = readStream.Read(btArray, 0, btArray.Length);// 向远程文件读第一次
                long currPostion = startPosition;

                while (contentSize > 0)// 如果读取长度大于零则继续读
                {
                    currPostion += contentSize;
                    int percent = (int)(currPostion * 100 / totalSize);
                    writeStream.Write(btArray, 0, contentSize);// 写入本地文件
                    contentSize = readStream.Read(btArray, 0, btArray.Length);// 继续向远程文件读取
                    position = (int)currPostion;
                    // 更新进度条(异步调用)
                    MethodInvoker m = new MethodInvoker(SynchProgressBar);
                    m.BeginInvoke(null, null);
                }

                //关闭流
                writeStream.Close();
                readStream.Close();
                label1.Invoke(new Action(() =>
                {
                    label1.Text = "下载更新完成";
                }));

                Thread.Sleep(100);

                if (new FileInfo($@"{filePath}/{filename}").Extension.Equals(".zip", StringComparison.CurrentCultureIgnoreCase))
                {
                    //解压
                    ZIPHelper.UnZip($@"{filePath}/{filename}", $@"{AppDomain.CurrentDomain.BaseDirectory}{unit.UnZipUrl}", null);
                }

                //修改更新后的版本号
                try
                {
                    config.AppSettings.Settings[$@"{unit.UnitName}"].Value = unit.UnitVersion.ToString();
                }
                catch
                {
                    //节点不存在，新增节点
                    config.AppSettings.Settings.Add(unit.UnitName, unit.UnitVersion.ToString());
                }

                config.Save();

                DeleteFolder(Application.StartupPath);
                Thread.Sleep(500);
                flag = true;        //返回true下载成功
            }
            catch (Exception ex)
            {
                writeStream.Close();
                flag = false;       //返回false下载失败
                MessageBox.Show("数据更新失败");
                Log log = new Log(AppDomain.CurrentDomain.BaseDirectory + @"/log/Log.txt");
                log.Error(ex.ToString());
            }
            return flag;
        }

        // 从文件头得到远程文件的长度
        private static int GetHttpLength(string url)
        {
            int length = 0;
            try
            {
                HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);// 打开网络连接
                HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();
                if (rsp.StatusCode == HttpStatusCode.OK)
                {
                    length = (int)rsp.ContentLength;// 从文件头得到远程文件的长度
                }
                rsp.Close();
                return length;
            }
            catch (Exception e)
            {
                return length;
            }
        }
        #endregion


        #region 读配置文件
        /// <summary>
        /// 读取配置文件
        /// </summary>
        void LoadConfig()
        {
            ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap
            {
                ExeConfigFilename = $@"{AppDomain.CurrentDomain.BaseDirectory}Config.config"
            };
            config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
            AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings");
            KeyValueConfigurationCollection keyValues = appSettings.Settings;

            ConfigItems = new Dictionary<string, string>();
            foreach (string key in keyValues.AllKeys)
            {
                ConfigItems.Add(key, keyValues[key].Value);
            }
        }
        #endregion


        #region 进度条开始
        /// <summary>
        /// 异步修改下载进度条
        /// </summary>
        private void SynchProgressBar()
        {
            this.skinProgressBar1.Minimum = 0;
            SetControlPropertyValue(this.skinProgressBar1, "Maximum", totalSize);
            SetControlPropertyValue(this.skinProgressBar1, "Value", position);
        }

        /// <summary>
        /// 设置控件指定属性
        /// </summary>
        /// <param name="oControl"></param>
        /// <param name="propName"></param>
        /// <param name="propValue"></param>
        private void SetControlPropertyValue(Control oControl, string propName, object propValue)
        {
            Action action = () =>
            {
                Type t = oControl.GetType();
                PropertyInfo[] props = t.GetProperties();
                foreach (PropertyInfo p in props)
                {
                    if (p.Name.ToUpper() == propName.ToUpper())
                    {
                        p.SetValue(oControl, propValue, null);
                    }
                }
            };
            this.Invoke(action);
        }
        #endregion 进度条结束


        #region 窗体事件
        /// <summary>
        /// 加载窗体
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Update_Load(object sender, EventArgs e)
        {
            //读取配置文件
            LoadConfig();
            //关闭主程序
            Process[] proc = Process.GetProcessesByName(ConfigItems["Exe"]);
            foreach (Process pro in proc)
            {
                pro.Kill();
            }
            //下载
            new Task(HttpUpdate).Start();
        }

        /// <summary>
        /// 关闭窗体
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Update_FormClosing(object sender, FormClosingEventArgs e)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
        #endregion 窗体事件结束 

    }
}
